import Asserts from "./asserts";
import Utils from "./utils";

/**
 * 功能参考了 java 中 java.util.Optional 类，
 *
 * 这个类，用于强调代码中可能存在的空值，用更优雅的方式处理：
 *
 * <pre>
 *    // pre
 *    public getAge(C c){
 *         if(c != null){
 *             B b = c.getB();
 *             if (b != null){
 *                return b.getAge();
 *             }
 *         }
 *         return null;
 *     }
 *
 *     // after
 *     const ret = Optional.ofNullable(c)
 *               .flatMap(e -> Optional.ofNullable(e.getB()))
 *               .flatMap(e -> Optional.ofNullable(e.getAge()))
 *               .orElse(-1);
 * </pre>
 *
 * --------------------------------------------------------------------------------------------------
 */
class Optional {

    /**
     *
     * @param value {{}}
     */
    constructor(value) {
        this.val = value;
    }

    /**
     * 构建一个 Optional，不允许内容为空
     *
     * @param value {{}}
     */
    static of(value) {
        Asserts.notEmpty(value, "value is undefined!");
        return new Optional(value);
    }

    /**
     * 构建一个 Optional，允许内容为空
     *
     * @param value {{}}
     */
    static ofNullable(value) {
        return value == null ? Optional.empty() : Optional.of(value);
    }

    /**
     * 构建一个空的 Optional
     */
    static empty() {
        return EMPTY;
    }

    /**
     * 将数据映射成新的 Optional 对象
     *
     * @param mapper 映射函数
     * @return {Optional}
     */
    map(mapper) {
        Asserts.notEmpty(mapper, 'mapper is undefined!');
        if (!this.isPresent()) {
            return Optional.empty();
        } else {
            return Optional.ofNullable(mapper(this.val));
        }
    }

    /**
     * 将数据映射成新的 Optional 对象
     *
     * @param mapper 映射函数
     * @return {Optional}
     */
    flatmap(mapper) {
        Asserts.notEmpty(mapper, 'mapper is undefined!');
        if (!this.isPresent()) {
            return Optional.empty();
        } else {
            let ret = mapper(this.val);
            Asserts.notEmpty(ret, 'optional is undefined!');
            return ret;
        }
    }

    /**
     * 判断是否为空
     *
     * @return {boolean}
     */
    isPresent() {
        return Utils.isEmpty(this.val);
    }

    /**
     * 终止函数，如果不为空，则执行回调函数
     *
     * @param call
     */
    ifPresent(call) {
        if (Utils.isNotEmpty(this.val)) {
            call(this.val)
        }
        return this;
    }


    /**
     * 终止函数，如果不为空，则执行回调函数
     *
     * @param call
     */
    ifNotBlank(call) {
        if (Utils.isNotBlank(this.val)) {
            call(this.val)
        }
        return this;
    }


    /**
     * 终止函数，如果不为空，则执行回调函数
     *
     * @param call
     */
    ifNotEmpty(call) {
        if (Utils.isNotEmpty(this.val)) {
            call(this.val)
        }
        return this;
    }

    /**
     * 终止函数：获取数值
     *
     * 逻辑上，不应该产生值为空的 Optional，如果为空，就直接抛出异常
     */
    get() {
        Asserts.notEmpty(this.val, "value is undefined!");
        return this.val;
    }

    /**
     * 终止函数，如果为空，则抛出异常
     *
     * @param err {Error} 错误信息
     */
    orElseThrow(err) {
        if (Utils.isEmpty(this.val)) {
            throw err;
        }
        return this;
    }

    /**
     * 终止函数，如果为空，则返回默认值
     */
    orElse(def) {
        return Utils.nvl(this.val, def);
    }
}

/**
 * 所有的空值，都由这个哑元元素代替
 * @type {Optional}
 */
const EMPTY = new Optional();

export default Optional;